package com.by.redis.utility.common;

import jakarta.annotation.Resource;
import lombok.Getter;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceClientConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.util.StringUtils;

import java.util.Objects;

@Configuration
@ConditionalOnProperty(name = "by.redis.common.enabled", havingValue = "true")
@EnableConfigurationProperties(ByCommonRedisProperties.class)
public class ByCommonRedisLettuceBuilder implements InitializingBean, DisposableBean {

    @Resource
    private ByCommonRedisProperties properties;

    @Getter
    private LettuceConnectionFactory factory;

    @Override
    public void afterPropertiesSet() {
        factory = createConnectionFactory();
    }

    @Override
    public void destroy() {
        if (Objects.nonNull(factory)) {
            factory.destroy();
        }
    }

    protected final ByCommonRedisProperties getProperties() {
        return this.properties;
    }

    public LettuceConnectionFactory createConnectionFactory() {
        LettuceClientConfiguration clientConfig = getLettuceClientConfiguration(getProperties().getLettuce().getPool());
        LettuceConnectionFactory lettuceConnectionFactory = createLettuceConnectionFactory(clientConfig);
        lettuceConnectionFactory.afterPropertiesSet();
        return lettuceConnectionFactory;
    }

    private LettuceConnectionFactory createLettuceConnectionFactory(LettuceClientConfiguration clientConfiguration) {
        return new LettuceConnectionFactory(getStandaloneConfig(), clientConfiguration);
    }

    private RedisStandaloneConfiguration getStandaloneConfig() {
        RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
        config.setHostName(properties.getHost());
        config.setPort(this.properties.getPort());
        if (org.apache.commons.lang3.StringUtils.isNotBlank(this.properties.getUsername())) {
            config.setUsername(this.properties.getUsername());
        }
        config.setPassword(RedisPassword.of(this.properties.getPassword()));
        config.setDatabase(this.properties.getDatabase());
        return config;
    }


    private LettuceClientConfiguration getLettuceClientConfiguration(ByCommonRedisProperties.Pool pool) {
        LettuceClientConfiguration.LettuceClientConfigurationBuilder builder = createBuilder(pool);
        applyProperties(builder);
        return builder.build();
    }

    private LettuceClientConfiguration.LettuceClientConfigurationBuilder createBuilder(ByCommonRedisProperties.Pool pool) {
        if (pool == null) {
            return LettuceClientConfiguration.builder();
        }
        return new PoolBuilderFactory().createBuilder(pool);
    }

    private LettuceClientConfiguration.LettuceClientConfigurationBuilder applyProperties(
            LettuceClientConfiguration.LettuceClientConfigurationBuilder builder) {
        if (getProperties().isSsl()) {
            builder.useSsl();
        }
        if (getProperties().getTimeout() != null) {
            builder.commandTimeout(getProperties().getTimeout());
        }
        if (getProperties().getLettuce() != null) {
            ByCommonRedisProperties.Lettuce lettuce = getProperties().getLettuce();
            if (lettuce.getShutdownTimeout() != null && !lettuce.getShutdownTimeout().isZero()) {
                builder.shutdownTimeout(getProperties().getLettuce().getShutdownTimeout());
            }
        }
        if (StringUtils.hasText(getProperties().getClientName())) {
            builder.clientName(getProperties().getClientName());
        }
        return builder;
    }

    /**
     * Inner class to allow optional commons-pool2 dependency.
     */
    private static class PoolBuilderFactory {

        LettuceClientConfiguration.LettuceClientConfigurationBuilder createBuilder(ByCommonRedisProperties.Pool properties) {
            return LettucePoolingClientConfiguration.builder().poolConfig(getPoolConfig(properties));
        }

        private GenericObjectPoolConfig<?> getPoolConfig(ByCommonRedisProperties.Pool properties) {
            GenericObjectPoolConfig<?> config = new GenericObjectPoolConfig<>();
            config.setMaxTotal(properties.getMaxActive());
            config.setMaxIdle(properties.getMaxIdle());
            config.setMinIdle(properties.getMinIdle());
            if (properties.getTimeBetweenEvictionRuns() != null) {
                config.setTimeBetweenEvictionRuns(properties.getTimeBetweenEvictionRuns());
            }
            if (properties.getMaxWait() != null) {
                config.setMaxWait(properties.getMaxWait());
            }
            return config;
        }

    }

}